<?php defined('BASEPATH') OR exit('No direct script access allowed');

class wxjssdk
{
	//allen
    // public $appId="wx79d1c1ab670c27a8";
    // public $appSecret="ad9f14332bfeeb4c64454273a5aea409";
	//customer
	public $CI;
	public  $appid;
    public  $appsecret;
	public $mchid;
	public $key;
	public function __construct(){
         if (!isset($this->CI)){
	        $this->CI =& get_instance();
		 }
		 $this->appid=$this->CI->config->item("appid");
		 $this->appsecret=$this->CI->config->item("appsecret");
		 $this->key=$this->CI->config->item("key");
		 $this->mchid=$this->CI->config->item("mchid");
    }
    public function getSignPackage()
    {
        $jsapiTicket = $this->getJsApiTicket();
        // 注意 URL 一定要动态获取，不能 hardcode.
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
        $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        $timestamp = time();
        $nonceStr = $this->createNonceStr();
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
        $signature = sha1($string);
        $signPackage = array(
            "appId" => $this->appid,
            "nonceStr" => $nonceStr,
            "timestamp" => $timestamp,
            "url" => $url,
            "signature" => $signature,
            "rawString" => $string
        );
        return $signPackage;
    }

    public function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

     public function getJsApiTicket()
    {
        // jsapi_ticket 应该全局存储与更新，以下代码以写入到文件中做示例
        $data = (object)json_decode(file_get_contents("jssdk_jsapi_ticket.json"));
        if ($data->expire_time < time()) {
            $accessToken = $this->getAccessToken();
            // 如果是企业号用以下 URL 获取 ticket
            // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken";
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
            $res = json_decode($this->httpGet($url));
            $ticket = $res->ticket;
            if ($ticket) {
                $data->expire_time = time() + 7000;
                $data->jsapi_ticket = $ticket;
                $fp = fopen("jssdk_jsapi_ticket.json", "w");
                fwrite($fp, json_encode($data));
                fclose($fp);
            }
        } else {
            $ticket = $data->jsapi_ticket;
        }

        return $ticket;
    }

    public function getAccessToken()
    {
        // access_token 应该全局存储与更新，以下代码以写入到文件中做示例
        $data = (object)json_decode(file_get_contents("jssdk_access_token.json"));
        if ($data->expire_time < time()) {
            // 如果是企业号用以下URL获取access_token
            // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appid&secret=$this->appsecret";
            $res = json_decode($this->httpGet($url));
            $fp = fopen("jssdk_access_token.json", "w");
            $access_token = $res->access_token;
            if ($access_token) {
                $data->expire_time = time() + 7000;
                $data->access_token = $access_token;
                $fp = fopen("jssdk_access_token.json", "w");
                fwrite($fp, json_encode($data));
                fclose($fp);
            }
        } else {
            $access_token = $data->access_token;
        }
        return $access_token;
    }
    public function httpGet($url)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);

        $res = curl_exec($curl);
        curl_close($curl);

        return $res;
    }
    	/**
	 * 
	 * 统一下单，WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填
	 * appid、mchid、spbill_create_ip、nonce_str不需要填入
	 * @param WxPayUnifiedOrder $inputObj
	 * @param int $timeOut
	 * @throws WxPayException
	 * @return 成功时返回，其他抛异常
	 */
	public  function unifiedOrder($total_fee, $timeOut = 6)
	{
		$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
		$appid=$this->appid;
		$attach="test";//附加参数，可作为自定义参数
		$body="礼物赠送";//商品名称
		$goods_tag="test";
		$mch_id=$this->mchid ;//店铺号
		$nonce_str=$this->createNonceStr();//随机字符串 
		$notify_url="http://paysdk.weixin.qq.com/example/notify.php";
		$open_id=$this->CI->session->userdata("user_open_id");//用户的OPEN_ID
		$out_trade_no=$this->mchid.date("YmdHis");//商户订单号 
		$spbill_create_ip="118.244.206.21";//终端IP
		$time_expire=date("YmdHis", time() + 600);//交易终止时间
		$time_start=date("YmdHis");//交易开始时间
		$total_fee=$total_fee*100;//交易金额,以分为单位;
		//$total_fee=1;  //测试时用的1分，正式发布后删除这句
		$trade_type="JSAPI";
		$key=$this->key;
		$string="appid=".$appid.
	    		"&attach=".$attach.
	    		"&body=".$body.
	    		"&goods_tag=".$goods_tag.
	    		"&mch_id=".$mch_id.
	    		"&nonce_str=".$nonce_str.
	    		"&notify_url=".$notify_url.
	    		"&openid=".$open_id.
	    		"&out_trade_no=".$out_trade_no.
	    		"&spbill_create_ip=".$spbill_create_ip.
	    		"&time_expire=".$time_expire.
	    		"&time_start=".$time_start.
	    		"&total_fee=".$total_fee.
	    		"&trade_type=JSAPI".
	    		"&key=".$key;
		$string = md5($string);
		$signature = strtoupper($string); //生成签名
		$xml="<xml>
		   <appid>$appid</appid>
		   <attach>$attach</attach>
		   <body>$body</body>
		   <goods_tag>$goods_tag</goods_tag>
		   <mch_id>$mch_id</mch_id>
		   <nonce_str>$nonce_str</nonce_str>
		   <notify_url>$notify_url</notify_url>
		   <openid>$open_id</openid>
		   <out_trade_no>$out_trade_no</out_trade_no>
		   <spbill_create_ip>$spbill_create_ip</spbill_create_ip>
		   <time_expire>$time_expire</time_expire>
		   <time_start>$time_start</time_start>
		   <total_fee>$total_fee</total_fee>
		   <trade_type>JSAPI</trade_type>
		   <sign>$signature</sign>
		</xml>";
		$startTimeStamp = $this->getMillisecond();//请求开始时间
		$response = $this->postXmlCurl($xml, $url, false, $timeOut);
		$xml_arr=simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA);
		$xml_arr->timeStamp=time();
		$xml_arr->nonceStr=$nonce_str;
		$string="appId=".$appid.
				"&nonceStr=".$xml_arr->nonceStr.
				"&package=prepay_id=".$xml_arr->prepay_id.
				"&signType=MD5".
				"&timeStamp=".$xml_arr->timeStamp.
				"&key=".$key;
		$string = md5($string);
		$paySign = strtoupper($string); //生成签名
		$xml_arr->paySign=$paySign;
		$json =json_encode($xml_arr);
		return $json;
	}
 
		/**
	 * 以post方式提交xml到对应的接口url
	 * 
	 * @param string $xml  需要post的xml数据
	 * @param string $url  url
	 * @param bool $useCert 是否需要证书，默认不需要
	 * @param int $second   url执行超时时间，默认30s
	 */
	private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
	{		
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
		curl_setopt($ch,CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验
		//设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		//要求结果为字符串且输出到屏幕上
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	
		if($useCert == true){
			//设置证书
			//使用证书：cert 与 key 分别属于两个.pem文件
			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLCERT, "../../cache/cert/apiclient_cert.pem");
			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLKEY, "../../cache/cert/apiclient_key.pem");
		}
		//post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		//运行curl
		$data = curl_exec($ch);
		
		//返回结果
		if($data){
			curl_close($ch);
			return $data;
		} else { 
			$error = curl_errno($ch);
			curl_close($ch);
		}
	}
	
	/**
	 * 获取毫秒级别的时间戳
	 */
	private static function getMillisecond()
	{
		//获取毫秒的时间戳
		$time = explode ( " ", microtime () );
		$time = $time[1] . ($time[0] * 1000);
		$time2 = explode( ".", $time );
		$time = $time2[0];
		return $time;
	}
	/**
	 * 输出xml字符
	 * @throws WxPayException
	**/
	public function ToXml($arr)
	{
		if(!is_array($arr) 
			|| count($arr) <= 0)
		{
    		die();
    	}
    	$xml = "<xml>";
    	foreach ($arr as $key=>$val)
    	{
    		if (is_numeric($val)){
    			$xml.="<".$key.">".$val."</".$key.">";
    		}else{
    			$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    		}
        }
        $xml.="</xml>";
        return $xml; 
	}
}

